/**
 * Copyright 2018 精通有道开源 http://www.jtyd.io
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.github.lyd.admin.provider.controller;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.abel533.sql.SqlMapper;
import com.github.lyd.admin.provider.entity.WxUserInfoEntity;
import com.github.lyd.admin.provider.service.WxUserInfoService;
import com.github.lyd.admin.provider.utils.CUtils;
import com.github.lyd.admin.provider.utils.R;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.collections.map.LinkedMap;
import org.apache.commons.lang.StringUtils;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import weixin.popular.api.SnsAPI;
import weixin.popular.bean.message.EventMessage;
import weixin.popular.bean.sns.Jscode2sessionResult;
import weixin.popular.bean.wxa.WxaDUserInfo;
import weixin.popular.bean.xmlmessage.XMLMessage;
import weixin.popular.bean.xmlmessage.XMLTextMessage;
import weixin.popular.support.ExpireKey;
import weixin.popular.support.expirekey.DefaultExpireKey;
import weixin.popular.util.SignatureUtil;
import weixin.popular.util.WxaUtil;
import weixin.popular.util.XMLConverUtil;
import zuo.biao.apijson.parser.APIJSONProvider;
import zuo.biao.apijson.parser.SQLExplorer;
import zuo.biao.apijson.parser.SQLProviderException;
import zuo.biao.apijson.parser.StatementType;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 系统用户
 * 
 * @author yaowu
 * @email sunlightcs@gmail.com
 * @date 2016年10月31日 上午10:40:10
 */
@RestController
@RequestMapping("/api/wxapp")
public class WeiXinController {

	@Value("${spring.weixin.xiao-cx.appid}")
	private String appid;

	@Value("${spring.weixin.xiao-cx.secret}")
	private String secret;

	@Value("${spring.weixin.medical_question}")
	private String medical_question;

	@Value("${spring.mongdb.userName}")
	private String mongdbUserName;

	@Value("${spring.mongdb.password}")
	private String mongdbPassword;

	@Value("${spring.mongdb.database}")
	private String mongdbDatabase;

	@Value("${spring.mongdb.host}")
	private String mongdbHost;

	@Value("${spring.mongdb.port}")
	private int mongdbPort;

	@Autowired
	private WxUserInfoService wxUserInfoService;
	
	@Autowired
    SqlMapper sqlMapper;
	/**
	 *
	 */
	private static final long serialVersionUID = 1L;

	//从官方获取
	private String token = "cdfajlecEHJCLE8237242";

	//重复通知过滤
	private static ExpireKey expireKey = new DefaultExpireKey();

	/**
	 * 微信公众平台 服务器消息验证
	 */
	@RequestMapping(value = "/mp", method = RequestMethod.GET)
	protected void mp(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		ServletInputStream inputStream = request.getInputStream();
		ServletOutputStream outputStream = response.getOutputStream();
		String signature = request.getParameter("signature");
		String timestamp = request.getParameter("timestamp");
		String nonce = request.getParameter("nonce");
		String echostr = request.getParameter("echostr");

		//首次请求申请验证,返回echostr
		if(echostr!=null){
			outputStreamWrite(outputStream,echostr);
			return;
		}

		//验证请求签名
		if(!signature.equals(SignatureUtil.generateEventMessageSignature(token,timestamp,nonce))){
			System.out.println("The request signature is invalid");
			return;
		}

		if(inputStream!=null){
			//转换XML
			EventMessage eventMessage = XMLConverUtil.convertToObject(EventMessage.class,inputStream);
			String key = eventMessage.getFromUserName() + "__"
					+ eventMessage.getToUserName() + "__"
					+ eventMessage.getMsgId() + "__"
					+ eventMessage.getCreateTime();
			if(expireKey.exists(key)){
				//重复通知不作处理
				return;
			}else{
				expireKey.add(key);
			}

			//创建回复
			XMLMessage xmlTextMessage = new XMLTextMessage(
					eventMessage.getFromUserName(),
					eventMessage.getToUserName(),
					"你好");
			//回复
			xmlTextMessage.outputStreamWrite(outputStream);
			return;
		}
		outputStreamWrite(outputStream,"");
	}

	/**
	 * 数据流输出
	 * @param outputStream
	 * @param text
	 * @return
	 */
	private boolean outputStreamWrite(OutputStream outputStream, String text){
		try {
			outputStream.write(text.getBytes("utf-8"));
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		return true;
	}

	/**
	 * 微信小程序用户授权
	 */
	@RequestMapping(value = "/session", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
	public R session(@RequestBody JSONObject jsonParam){
		Jscode2sessionResult jscode2sessionResult = SnsAPI.jscode2session(appid,secret,jsonParam.get("code").toString());
		WxaDUserInfo wxaDUserInfo = WxaUtil.decryptUserInfo(
				jscode2sessionResult.getSession_key(),
				jsonParam.get("data").toString(),
				jsonParam.get("iv").toString()
		);
		WxUserInfoEntity wxUserInfo = wxUserInfoService.selectById(wxaDUserInfo.getOpenId());
		if(null == wxUserInfo){
			wxUserInfo = new WxUserInfoEntity();
			wxUserInfo.setAvatarUrl(wxaDUserInfo.getAvatarUrl());
			wxUserInfo.setCity(wxaDUserInfo.getCity());
			wxUserInfo.setCountry(wxaDUserInfo.getCountry());
			wxUserInfo.setGender(wxaDUserInfo.getGender());
			wxUserInfo.setLanguage(wxaDUserInfo.getLanguage());
			wxUserInfo.setNickname(wxaDUserInfo.getNickName());
			wxUserInfo.setOpenId(wxaDUserInfo.getOpenId());
			wxUserInfo.setProvince(wxaDUserInfo.getProvince());
			if(StringUtils.isEmpty(wxaDUserInfo.getUnionId())){
				wxUserInfo.setUnionId(wxaDUserInfo.getUnionId());
			}else{
				wxUserInfo.setUnionId("");
			}
			wxUserInfoService.insertWxUserInfo(wxUserInfo);
		}
		System.out.println(wxaDUserInfo.getOpenId());
		Map<String, Object> map = new HashMap<>();
		String sessionId = CUtils.get().getSessionId();
		map.put("sessionId", sessionId);
		map.put("unionId", wxaDUserInfo.getUnionId());
		map.put("nickName", wxaDUserInfo.getNickName());
		map.put("avatarUrl", wxaDUserInfo.getAvatarUrl());
		return R.ok(map);
	}

	/**
	 * 微信小程序每日资讯
	 * 1、mongdb 数据库信息：
			 MONGO_HOST = "47.98.183.198"
			 MONGO_PORT = 4001
			 MONGO_数据库 = jtyd
			 MONGO_数据表 = daily_new
			 MONGO_用户名 = jtyd_w
			 MONGO_密码 = "jtyd987_123"
	   2、根据 daily_new date_time 查询 content 并返回小程序
	 */
	@RequestMapping(value = "/daily_new", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
	public R daily_new(@RequestParam("newsDate") String newsDate) throws ParseException {
		MongoCredential credential = MongoCredential.createCredential(mongdbUserName,mongdbDatabase,mongdbPassword.toCharArray());
		MongoClient mongoClient = new MongoClient(new ServerAddress(mongdbHost,mongdbPort), Arrays.asList(credential));
		System.out.println("Connect to database successfully");
		MongoDatabase mongoDatabase = mongoClient.getDatabase(mongdbDatabase);
		java.text.DateFormat format1 = new java.text.SimpleDateFormat("yyyy-MM-dd");
		if(StringUtils.isEmpty(newsDate)) {
			newsDate = format1.format(new Date());
		}
		BasicDBObject query = new BasicDBObject("date_time",newsDate);
		JSONArray json = new JSONArray();

/*		MongoCollection daily_newCollection = mongoDatabase.getCollection("daily_new");
		FindIterable<Document> daily_newIterable = daily_newCollection.find(query);
		MongoCursor<Document> daily_newCursor = daily_newIterable.iterator();
        String news_content = "";
		while (daily_newCursor.hasNext()){
			news_content = daily_newCursor.next().get("content").toString();
		}*/

		MongoCollection finvestCollection = mongoDatabase.getCollection("finvest");
		FindIterable<Document> findIterable = finvestCollection.find(new BasicDBObject("create_time",getLikeStr(newsDate)));
		MongoCursor<Document> finvestCursor = findIterable.iterator();
		while (finvestCursor.hasNext()){
			try{
				JSONObject jo = new JSONObject();
				Document document = finvestCursor.next();
				jo.put("id", document.getObjectId("_id").toString());
				jo.put("title", document.getString("title"));
				jo.put("tag", "投融资");
				json.add(jo);
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		MongoCollection weixinCollection = mongoDatabase.getCollection("weixin");
		FindIterable<Document> weixinIterable = weixinCollection.find(new BasicDBObject("publish_time",getLikeStr(newsDate)));
		MongoCursor<Document> weixinCursor = weixinIterable.iterator();
		while (weixinCursor.hasNext()){
			try{
				JSONObject jo = new JSONObject();
				Document document = weixinCursor.next();
				jo.put("id", document.getObjectId("_id").toString());
				jo.put("title", document.getString("title"));
				String tag = "";
				try{
					tag = weixinCursor.next().get("tag").toString();
				} catch (Exception e) {
					e.printStackTrace();
				}
				jo.put("tag", tag);
				json.add(jo);
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		MongoCollection toutiaoCollection = mongoDatabase.getCollection("toutiao");
		FindIterable<Document> toutiaoIterable = toutiaoCollection.find(new BasicDBObject("publish_time",getLikeStr(newsDate)));
		MongoCursor<Document> toutiaoCursor = toutiaoIterable.iterator();
		while (toutiaoCursor.hasNext()){
			try {
				JSONObject jo = new JSONObject();
				Document document = toutiaoCursor.next();
				jo.put("id", document.getObjectId("_id").toString());
				jo.put("title", document.getString("title"));
				String tag = "";
				try {
					tag = weixinCursor.next().get("tag").toString();
				} catch (Exception e) {
					e.printStackTrace();
				}
				jo.put("tag", tag);
				json.add(jo);
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

		return R.ok().put("news_content", json.toJSONString());
	}

	private BasicDBObject getLikeStr(String findStr) {
		Pattern pattern = Pattern.compile("^.*" + findStr + ".*$", Pattern.CASE_INSENSITIVE);
		return new BasicDBObject("$regex", pattern);
	}

	/**
	 * 医疗问答接口
	 */
	@RequestMapping(value = "/medical_question", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
	public R medicalQuestion(@RequestParam("question") String question){
		String url = medical_question+"?question="+question;
		OkHttpClient okHttpClient = new OkHttpClient();
		Request request = new Request.Builder()
				.url(url)
				.build();
		Call call = okHttpClient.newCall(request);
		Map<String,Object> map = null;
		try {
			Response response = call.execute();
			map = CUtils.get().jsonString2map(response.body().string());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return R.ok(map);
	}


	/**
	 * 增加深度事件
	 */
	@RequestMapping(value = "/add_event_topics", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
	public R add_event_topics(@RequestParam("topic") String topic) throws ParseException {
		MongoCredential credential = MongoCredential.createCredential(mongdbUserName,mongdbDatabase,mongdbPassword.toCharArray());
		MongoClient mongoClient = new MongoClient(new ServerAddress(mongdbHost,mongdbPort), Arrays.asList(credential));
		System.out.println("Connect to database successfully");
		MongoDatabase mongoDatabase = mongoClient.getDatabase(mongdbDatabase);

		MongoCollection finvestCollection = mongoDatabase.getCollection("eventmonitor_topics");
		Document document = new Document("topic", topic).
				append("status", 0).
				append("data_nodes", "").
				append("data_edges", "").
				append("html", "").
				append("create_time", new Date()).
				append("open_id", "");
		finvestCollection.insertOne(document);
		return R.ok();
	}

	/**
	 * 查询事件列表
	 */
	@RequestMapping(value = "/event_topics_list", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
	public R event_topics_list() throws ParseException {
		MongoCredential credential = MongoCredential.createCredential(mongdbUserName,mongdbDatabase,mongdbPassword.toCharArray());
		MongoClient mongoClient = new MongoClient(new ServerAddress(mongdbHost,mongdbPort), Arrays.asList(credential));
		System.out.println("Connect to database successfully");
		MongoDatabase mongoDatabase = mongoClient.getDatabase(mongdbDatabase);

		MongoCollection finvestCollection = mongoDatabase.getCollection("eventmonitor_topics");
		FindIterable<Document> findIterable = finvestCollection.find();
		MongoCursor<Document> finvestCursor = findIterable.iterator();
		JSONArray json = new JSONArray();
		while (finvestCursor.hasNext()){
			try{
				JSONObject jo = new JSONObject();
				Document document = finvestCursor.next();
				jo.put("id", document.getObjectId("_id").toString());
				jo.put("topic", document.get("topic"));
				json.add(jo);
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		return R.ok().put("news_content", json.toJSONString());
	}

	/**
	 * 查询事件列表
	 */
	@RequestMapping(value = "/event_topics_news_list", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
	public R event_topics_news_list(@RequestParam("topic") String topic) throws ParseException {
		MongoCredential credential = MongoCredential.createCredential(mongdbUserName,mongdbDatabase,mongdbPassword.toCharArray());
		MongoClient mongoClient = new MongoClient(new ServerAddress(mongdbHost,mongdbPort), Arrays.asList(credential));
		System.out.println("Connect to database successfully");
		MongoDatabase mongoDatabase = mongoClient.getDatabase(mongdbDatabase);

		MongoCollection finvestCollection = mongoDatabase.getCollection("eventmonitor");
		FindIterable<Document> findIterable = finvestCollection.find(new BasicDBObject("keyword",topic)).sort(new BasicDBObject("news_date",1));
		MongoCursor<Document> finvestCursor = findIterable.iterator();
		JSONArray json = new JSONArray();
		while (finvestCursor.hasNext()){
			try{
				JSONObject jo = new JSONObject();
				Document document = finvestCursor.next();
				jo.put("id", document.getObjectId("_id").toString());
				jo.put("news_title", document.get("news_title"));
				jo.put("news_date", document.get("news_date"));
				json.add(jo);
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		return R.ok().put("event_topics_news_list", json.toJSONString());
	}

	/**
	 * 查询事件详情图文件
	 */
	@RequestMapping(value = "/event_topics_detail", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
	public R event_topics_detail(@RequestParam("topic") String topic) throws ParseException {
		MongoCredential credential = MongoCredential.createCredential(mongdbUserName,mongdbDatabase,mongdbPassword.toCharArray());
		MongoClient mongoClient = new MongoClient(new ServerAddress(mongdbHost,mongdbPort), Arrays.asList(credential));
		System.out.println("Connect to database successfully");
		MongoDatabase mongoDatabase = mongoClient.getDatabase(mongdbDatabase);

		MongoCollection finvestCollection = mongoDatabase.getCollection("eventmonitor_topics");
		BasicDBObject query = new BasicDBObject("topic",topic);
		FindIterable<Document> findIterable = finvestCollection.find(query);
		MongoCursor<Document> finvestCursor = findIterable.iterator();
		JSONArray json = new JSONArray();
		while (finvestCursor.hasNext()){
			try{
				JSONObject jo = new JSONObject();
				Document document = finvestCursor.next();
				jo.put("id", document.getObjectId("_id").toString());
				jo.put("html", document.get("html"));
				json.add(jo);
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		return R.ok().put("news_content", json.toJSONString());
	}
	
	/**
	 * APIJSON API
	 * 通过APIJSON协议，对数据库进行操作
	 */
	@RequestMapping("/apijson_query")
	@ResponseBody
	public List<LinkedMap> apijson_query(@RequestBody JSONObject request){
		APIJSONProvider provider = new APIJSONProvider(request);
		provider.setStatementType(StatementType.SELECT);
		SQLExplorer builder = new SQLExplorer(provider);
		List<LinkedMap> list = new ArrayList<>();
		try {
			String sql = builder.getSQL();
			if(request.get("page") != null)
				list = (List<LinkedMap>) sqlMapper.selectList( sql + " limit (#{page.number}-1)*#{page.size},#{page.size}", request, LinkedMap.class);
			else
				list = (List<LinkedMap>) sqlMapper.selectList(sql + " limit 0,100", request, LinkedMap.class);
		} catch (SQLProviderException e) {
			// TODO Auto-generated catch block
			list.add(error(400, e.getErrorMsgs().toString()));
		}
		return list;
	}
	
	@RequestMapping("/apijson_insert")
	@ResponseBody
	public List<LinkedMap> apijson_insert(@RequestBody JSONObject request){
		APIJSONProvider provider = new APIJSONProvider(request);
		provider.setStatementType(StatementType.INSERT);
		SQLExplorer builder = new SQLExplorer(provider);
		List<LinkedMap> list = new ArrayList<>();
		try {
			String sql = builder.getSQL();
			int i = sqlMapper.insert(sql);
		} catch (SQLProviderException e) {
			// TODO Auto-generated catch block
			list.add(error(400, e.getErrorMsgs().toString()));
		}
		return list;
	}
	
	@RequestMapping("/apijson_update")
	@ResponseBody
	public List<LinkedMap> apijson_update(@RequestBody JSONObject request){
		APIJSONProvider provider = new APIJSONProvider(request);
		provider.setStatementType(StatementType.UPDATE);
		SQLExplorer builder = new SQLExplorer(provider);
		List<LinkedMap> list = new ArrayList<>();
		try {
			String sql = builder.getSQL();
			int i = sqlMapper.update(sql);
		} catch (SQLProviderException e) {
			// TODO Auto-generated catch block
			list.add(error(400, e.getErrorMsgs().toString()));
		}
		return list;
	}
	
	@RequestMapping("/apijson_delete")
	@ResponseBody
	public List<LinkedMap> apijson_delete(@RequestBody JSONObject request){
		APIJSONProvider provider = new APIJSONProvider(request);
		provider.setStatementType(StatementType.DELETE);
		SQLExplorer builder = new SQLExplorer(provider);
		List<LinkedMap> list = new ArrayList<>();
		try {
			String sql = builder.getSQL();
			int i = sqlMapper.delete(sql);
		} catch (SQLProviderException e) {
			// TODO Auto-generated catch block
			list.add(error(400, e.getErrorMsgs().toString()));
		}
		return list;
	}
	
	private LinkedMap error(int code, String msg) {
		LinkedMap rsp = new LinkedMap();
		rsp.put("code", code);
		rsp.put("msg", msg);
		return rsp;
	}
}
